package com.jy.framework.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jy.framework.annotation.RSASign;
import com.jy.framework.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.security.KeyPair;
import java.util.Arrays;
import java.util.Comparator;

/**
 * @Author: JuiceYan
 * @Description: 加密工具类
 * @Class: RSAUtils
 * @Date: 17:34 2018/11/21
 */
@Slf4j
public class RSAUtils {
    public static final String PRIVATE_KEY = "privateKey";
    public static final String PUBLIC_KEY = "public staticKey";
    public static final String SIGN_PRIVATE_KEY = "signPrivateKey";
    public static final String SIGN_PUBLIC_KEY = "signPublicKey";
    public static final String LINGRIT_RSA_KEY_MAP = "jy.rsa.keyMap";
    static String privateKey= "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALQTm7hp2v5gELB86xU7N/ubgKx04seKbUDRqyDf5kRR0lUR7x4QkBvqumelzUGjwlFMKm2UCcX8C32x5SHqOAMRYr5l0tPG4EgszfuYQq3euFvXVShlCoaPt1dLfuhg3HqYlMO3CfAItj5NoXqxyIf0C0mjwd/wXKoZpu15XhjXAgMBAAECgYALx4iVLJ99qk0NLm62SdRkqgxlu5OmF+EDr3NxSBQo/e0zUWQB5HV4zgb5hrgBhjPM252e42MsR0t9ImjpqgeV17I5hFlN1MOfLbgn8fKX4O/blr8NkpNCfZkcmw01p37Fp2r0bnXNnOO/ENyOLZjAjOKjtplM484OaB5Zo3P+CQJBANpe2EO+6e3IT0M5MOm+CpxuIXIua4ynvt5iqJBbpJxsiXcJs1aHuSjOieW4x7d6Wm9VBZnqy2PV3pR4MDalkT0CQQDTG3lMIM0e7wLOCE0nWZfhoF216G957RO4Cuv9QPX5zCI0rO5+WjNeqNO4XkbDq4EU+Rx+GvKQh9t0LZ6E2QujAkBJQ7EDabZKlP4i67lBcwVUXo7SJEjpNA6RxkAPzu93m+KzEYod6gNxcXpKnKhFIsPcvoXWkjQTFtpKrdhfPUVxAkBQ++gjWS33IO9thGe8obUPHAr2/qzNFDKNVrtDEwmJg1LX7EMte4LbXF6kCp6Q75j8J7hTdBj7cvKv9dQq2GyLAkBAxd1u0tWI2a+9vujm86SrpxBZbIAQnpLAuTrYI31hiJB+e2puX1dE0du0T56xw5R/q/l3sQRI684tL1vNeJ8v";
    static String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0E5u4adr+YBCwfOsVOzf7m4CsdOLHim1A0asg3+ZEUdJVEe8eEJAb6rpnpc1Bo8JRTCptlAnF/At9seUh6jgDEWK+ZdLTxuBILM37mEKt3rhb11UoZQqGj7dXS37oYNx6mJTDtwnwCLY+TaF6sciH9AtJo8Hf8FyqGabteV4Y1wIDAQAB";
    /**
     * @Author: JuiceYan
     * @Description: 只执行一次，写入数据库
     * @param: []
     * @return: void
     * @throws:
     * @Date: 17:48 2018/11/21
     */
    private void addKeys() {
        String algorithm = "RSA";
//      生成  秘钥
        KeyPair pair = SecureUtil.generateKeyPair(algorithm);
        String privateKey = Base64.encode(pair.getPrivate().getEncoded());
        String publicKey = Base64.encode(pair.getPublic().getEncoded());
        System.out.println("privateKey-"+privateKey);
        System.out.println("publicKey-"+publicKey);
//        List<SysConfigEntity> pps = new ArrayList<>();
//        SysConfigEntity privateKeyCfg = new SysConfigEntity();
//        privateKeyCfg.setParamKey("privateKey");
//        privateKeyCfg.setParamValue(privateKey);
//        pps.add(privateKeyCfg);
//
//        SysConfigEntity publicKeyCfg =new SysConfigEntity();
//        publicKeyCfg.setParamKey("publicKey");
//        publicKeyCfg.setParamValue( publicKey);
//        pps.add( publicKeyCfg);
//        sysConfigService.saveBatch(pps);
    }

    public static void main(String[] args) {
//        new RSAUtils().addKeys();
        String encrypt = encrypt("123,1,23131");
        String decrypt = decrypt(encrypt);
        System.out.println(decrypt);
    }
    /**
     * @Author: JuiceYan
     * @Description: RSA加密数据
     * @param: [dataStr]
     * @return: java.lang.String
     * @throws:
     * @Date: 11:56 2018/11/22
     */
    public static String encrypt(String dataStr) {
        byte[] data = StrUtil.bytes(dataStr, CharsetUtil.CHARSET_UTF_8);
        RSA rsa = new RSA(null, publicKey);
//        RSA rsa = new RSA(null, getPublicKey());
        //公钥加密，

        byte[] encrypt = rsa.encrypt(data, KeyType.PublicKey);
        String encryptStr = HexUtil.encodeHexStr(encrypt);
        return encryptStr;
    }

    /**
     * @Author: JuiceYan
     * @Description: 获取RSA公钥
     * @param: []
     * @return: java.lang.String
     * @throws:
     * @Date: 11:55 2018/11/22
     */
    public static String getPublicKey() {
        return getKey(PUBLIC_KEY);
    }

    /**
     * @Author: JuiceYan
     * @Description: 获取RSA私钥
     * @param: []
     * @return: java.lang.String
     * @throws:
     * @Date: 11:55 2018/11/22
     */
    public static String getPrivateKey() {
        return getKey(PRIVATE_KEY);
    }

    /**
     * @Author: JuiceYan
     * @Description: 根据key，获取配置中的对应秘钥
     * @param: [key]
     * @return: java.lang.String
     * @throws:
     * @Date: 11:54 2018/11/22
     */
    public static String getKey(String key) {
        String value = null;
        String config = Global.getConfig(LINGRIT_RSA_KEY_MAP);
        JSONObject keyMap = JSONObject.parseObject(config);
//        RedisUtils redisUtils = SpringContextUtils.getBean(RedisUtils.class);
        if (null != keyMap) {
            value = (String) keyMap.get(key);
        }
//        else if (null == value) {
//            value = redisUtils.get(RedisKeys.getKeys(key));
//        } else if (null == value) {
//            SysConfigService sysConfigService = SpringContextUtils.getBean(SysConfigService.class);
//            value = sysConfigService.getValue(key);
//            redisUtils.set(key, value);
//        }
        return value;
    }

    /**
     * @Author: JuiceYan
     * @Description: RSA解密
     * @param: [encryptStr]
     * @return: java.lang.String
     * @throws:
     * @Date: 11:56 2018/11/22
     */
    public static String decrypt(String encryptStr) {
        byte[] inputs = HexUtil.decodeHex(encryptStr);
//        RSA rsa1 = new RSA(getPrivateKey(), null);
        RSA rsa1 = new RSA(privateKey, null);
        byte[] decrypt = rsa1.decrypt(inputs, KeyType.PrivateKey);
        String output = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
        return output;
    }

    /**
     * @Author: JuiceYan
     * @Description: 为数据对象指定注解字段做签名
     * @param: [toSign]
     * @return: void
     * @throws:
     * @Date: 11:58 2018/11/22
     */
    public static <T> String signFields(T t) {
        String str = toFieldValuesStr(t);
        return sign(str);
    }

    /**
     * @Author: JuiceYan
     * @Description: 将所有字段值链接起来，为签名做准备
     * @param: [t]
     * @return: java.lang.String
     * @throws:
     * @Date: 16:37 2018/11/22
     */
    public static <T> String toFieldValuesStr(T t) {
        Class<?> target = t.getClass();
        Field[] declaredFields = target.getDeclaredFields();
        StringBuffer sign = new StringBuffer();
        Arrays.stream(declaredFields)
                .filter(f -> null != f.getAnnotation(RSASign.class))
                .sorted(Comparator.comparingInt(f -> f.getAnnotation(RSASign.class).index())).forEach(f -> {
            try {
                f.setAccessible(true);
                Object value = f.get(t);
                if (StringUtils.isNotBlank(String.valueOf(value))) {
                    sign.append(value);
                }
//                f.set(t,value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                log.error(e.getMessage(), e);
            }
        });
        return sign.toString();
    }

    /**
     * @Author: JuiceYan
     * @Description: 签名
     * @param: [str]
     * @return: java.lang.String
     * @throws:
     * @Date: 13:40 2018/11/22
     */
    public static String sign(String str) {
        byte[] data = StrUtil.bytes(str, CharsetUtil.CHARSET_UTF_8);
        Sign signEn = SecureUtil.sign(SignAlgorithm.MD5withRSA, getKey(SIGN_PRIVATE_KEY), getKey(SIGN_PUBLIC_KEY));
        String signedStr = HexUtil.encodeHexStr(signEn.sign(data));
        return signedStr;
    }

    /**
     * @Author: JuiceYan
     * @Description: 验证对象签名
     * @param: [t, sign]
     * @return: void
     * @throws:
     * @Date: 16:22 2018/11/22
     */
    public static <T> void verifySign(T t, String sign) {
        boolean verify = isVerify(t, sign);
        if (!verify) {
            log.error("签名失败:" + JSON.toJSONString(t));
            throw new MyException("签名失败");
        }
    }

    public static <T> boolean isVerify(T t, String sign) {
        String str = toFieldValuesStr(t);
        byte[] data = StrUtil.bytes(str, CharsetUtil.CHARSET_UTF_8);
        Sign signDe = SecureUtil.sign(SignAlgorithm.MD5withRSA, getKey(SIGN_PRIVATE_KEY), getKey(SIGN_PUBLIC_KEY));
        return signDe.verify(data, HexUtil.decodeHex(sign));
    }
}
